
"""
Singleton

This is the base class for any singleton objects that we need in a multi-
threaded environment. This class is inspired by the Singleton pattern
documented in Python cookbook. However, we can't copy it as that is as that
implementation doesn't work completely.

USAGE: All singleton class must be derived from Singleton class. Also, all
subclasses MUST chain to the Singleton's __init__ () constructor in their
constuctors in order to make sure that there is only one instance of the class 
across all threads.

NOTE: Singleton class cannot be dependent on any other Monsoon classes or even
ServerMessageMap.

"""

# Python standard module imports
import threading


class Singleton (object):

    # This message can't be located inside any message catelog.
    MSG_TWO_SINGLETON = u"System Error: Cannot instantiate another copy of singleton class [%s]."

    # class variables
    dInstanceList = {}  # one entry per Singleton class in whole system
    lLock = threading.Lock ()

    
    # This function is called when a new instance is created. This is 
    # overwritten to avoid creating more than one instance. However, it 
    # doesn't prevent calling the constructor twice. Hence, we still need to
    # override __init__. We override this method as a last defense. 
    def __new__ (cls, *args, **kwargs):
        if '_inst' not in vars(cls):
            cls._inst = object.__new__ (cls, *args, **kwargs)
        return cls._inst

    
    # All derived classes of Singleton MUST call this __init__ () method in
    # their constructors in order to make sure that there is only one instance 
    # of the class across all threads.
    def __init__ (self):
        Singleton.lLock.acquire ()
        try:
            if Singleton.dInstanceList.get (self.__class__.__name__) == None:
                Singleton.dInstanceList[self.__class__.__name__] = 1
            else:
                raise AssertionError (Singleton.MSG_TWO_SINGLETON % self.__class__.__name__)
        finally: 
            Singleton.lLock.release () 


    # Class method to get the only instance of the Singleton Object
    def getTheInstance (cls):
        return cls._inst 

    getTheInstance = classmethod (getTheInstance)

